﻿using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System.IdentityModel.Tokens.Jwt;

namespace FlyingEye.SignalRHubs
{
    /// <summary>
    /// 设备客户端中心
    /// 注意一个问题：在 DeviceHub 使用到的仓储，导航属性都会失效，所以我们使用了 IDeviceConfigRepository 来获取设备端的配置。
    /// 客户端掉线重连后，需要重新添加客户端映射
    /// </summary>
    [Authorize]
    public class DeviceHub : Hub<IDeviceClient>
    {
        private readonly HubJwtTokenParser _jwtTokenParser;

        private readonly DeviceResourceMapping _mapping;

        private readonly ILogger<DeviceHub> _logger;

        public DeviceHub(
            IMapper mapper,
            DeviceResourceMapping connectionMapping,
            HubJwtTokenParser jwtTokenParser,
            ILogger<DeviceHub> logger)
        {
            _mapping = connectionMapping;
            _jwtTokenParser = jwtTokenParser;
            _logger = logger;
        }

        /// <summary>
        /// 客户端连接时，添加映射
        /// </summary>
        public async override Task OnConnectedAsync()
        {
            var httpContext = this.Context.GetHttpContext();

            if (httpContext != null)
            {
                if (httpContext.Request.Headers.TryGetValue("Authorization", out var tokenFromHeader))
                {
                    var token = tokenFromHeader.ToString().Remove(0, "Bearer ".Length);
                    JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
                    var jwtSecurityToken = jwtSecurityTokenHandler.ReadJwtToken(token);

                    if (jwtSecurityToken.Payload.TryGetValue("resource_id", out var resourceId))
                    {
                        var stringResourceId = resourceId.ToString();

                        if (!string.IsNullOrWhiteSpace(stringResourceId))
                        {
                            // 设备端连接时，添加映射
                            if (_mapping.TryAdd(stringResourceId, this.Context.ConnectionId))
                            {
                                _logger.LogInformation($"客户端 {stringResourceId} 已连接！");
                            }
                        }
                    }
                }
            }

            await base.OnConnectedAsync();
        }

        /// <summary>
        /// 设备断开后，移除掉映射
        /// </summary>
        public async override Task OnDisconnectedAsync(Exception? exception)
        {
            if (_mapping.TryRemoveByConnectionId(this.Context.ConnectionId, out var clientName))
            {
                _logger.LogInformation($"客户端 {clientName} 已断开连接！");
            }

            await base.OnDisconnectedAsync(exception);
        }
    }
}
